#include <WiFi.h>
#include <WiFiMulti.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
#include<Arduino.h>
// #include<TFT_eSPI.h>
#include<time.h>
#include<string.h>

#include"xfont.h"
#include"get_date.h"
#include"get_weather.h"
#include"tft_show.h"
#include "key_tools.h"


// 自身时钟计时
#define SECOND 1000
#define MINUTE 1000*60
#define HOUR 1000*60*60
unsigned long second_hand = 0;
int8_t t_second;
int8_t t_minute;
int8_t t_hour;

// 记录更新时间
volatile unsigned long date_t;    // 日期更新时间
volatile unsigned long weather_t;    // 天气更新时间


XFont *_xFont;    //中文输出器对象

// 心知天气api-key和城市
String key = "S96GFB2OZeJnyuIaA";
String city = "chengdu";


// NTP config
const char *ntpServer = "ntp2.aliyun.com";   // 阿里云NTP服务器
const long gmtOffset_sec = 8 * 3600;    // 时区偏移量，北京是GMT+8
const int daylightOffset_sec = 0;       // 夏令时偏移量，中国无夏令时



// WiFi config
const char *ssid = "Tenda_D7BEA8";
const char *pwd = "tt174020";
WiFiMulti WiFiMulti;

// mqtt config
const char *mqtt_broker = "47.109.79.172";
const int mqtt_port = 1883;
const char *topic = "/lock";
WiFiClient espclient;
PubSubClient client(espclient);

// servo config
#define servo_pin 15   // GPIO2
#define servo_ON 60   // 开门的角度
#define servo_OFF 150 // 回位的角度
Servo servo;

void setup()
{
    Serial.begin(115200);
    Serial.println();

    // 初始化屏幕
    tft_init();
    // 中文输出对象
    _xFont = new XFont();
    // LED
    // pinMode(LED_BUILTIN, OUTPUT);
    // digitalWrite(LED_BUILTIN, HIGH); // 初始不亮

    // servo
    servo.setPeriodHertz(50);
    servo.attach(servo_pin, 500, 2500);
    servo.write(servo_OFF);
    delay(100);

    // wifi
    WiFi.mode(WIFI_STA); // 设置为STA模式
    WiFiMulti.addAP(ssid, pwd);
    WiFiMulti.addAP("Hi nova 9z", "12345678");
    WiFiMulti.addAP("donkeycar", "donkeycar");
    connect_wifi_multi();
    tft_display_loading_complete();

    Serial.printf("Connected to WiFi:%s", ssid);
    // 配置ntp服务器，开始同步时间
    configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

    tft_display_layout();

    // mqtt
    client.setServer(mqtt_broker, mqtt_port);
    client.setCallback(callback);
    connect_mqtt();
    client.subscribe(topic);

    // 蜂鸣器
    pinMode(beep_pin, OUTPUT);    // 绑定引脚模式为输出
    // 开机时获取一次时间
    printLocalTime();
}

void loop()
{
  char key = keypad.getKey();// 读取键盘

 // 如果有读到内容
  int i;
  if (key){
  Serial.print("Key Pressed : ");
  Serial.println(key);

  // *键回退
  if(key == '*'){
    if(idx != -1)
      input[idx--] = 0;       // 当前这一位清零并回退    
    Serial.print("回退");
    clear();    // 局部清屏
  }

  // #键清零
  else if(key == '#'){
    tft.setTextFont(7);
    for (i = 0; i < 5;i++)
      input[i] = 0;
    idx = -1;
    Serial.print("清零");
    clear();
  }

    
  else{
    input[++idx] = key;
  }
  for(i = 0; i < idx + 1; i ++){
    tft.setCursor(10 + i*50, 80, 6);
    tft.setTextColor(TFT_BLACK, TFT_WHITE);
    tft.printf("%c", input[i]);
  }
  
  if(idx==4){               // 密码的第五位已经记录完成，开始判断
    if(authenticated()){
      Serial.print("开门成功\n");
      tft.setCursor(50, 150, 4);
      tft.setTextColor(TFT_GREEN, TFT_WHITE);
      tft.printf("OPEN!");
      servo.write(servo_ON);
      Beep(true);
      delay(2000);
      servo.write(servo_OFF);
    }
    else{
      tft.setCursor(50, 150, 4);
      tft.setTextColor(TFT_RED, TFT_WHITE);
      tft.printf("FAIL!");
      Beep(false);
      delay(1000);
    }
    // 无论是否开门，位数已满清零
    for (i = 0; i < 5; i++)
      input[i] = 0;
    idx = -1;
    clear();
    }
  }


    while (!client.loop()) {
        connect_wifi_multi();
        connect_mqtt();
        client.subscribe(topic);
        // digitalWrite(LED_BUILTIN, LOW);
    }

    // 每两个小时更新一次天气
    if(millis() - weather_t > 7200000 || weather_t ==0){
      weather_t = millis();
      getWeather();
    }
    // 每24小时更新一次日期或时间
    if(millis() - date_t > 7200000 * 12 || date_t ==0){
      date_t = millis();
      printDate();
    }


    // client.loop();

    printLocalTime();



}

void connect_wifi_multi()
{
    WiFi.disconnect(true); // ESP32需要true参数来清除连接信息
    // digitalWrite(LED_BUILTIN, LOW);
    Serial.printf("connecting to WiFi......");
    while (WiFiMulti.run() != WL_CONNECTED) {
        // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        delay(500);
    }
    Serial.printf("connected to %s\n", WiFi.SSID().c_str());
    // digitalWrite(LED_BUILTIN, HIGH);
}

void connect_mqtt()
{
    int led_state = HIGH;
    while (!client.connected()) {
        // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        String client_id = "esp32-client-" + String(WiFi.macAddress());
        Serial.printf("client %s is connecting to broker %s ... ", client_id.c_str(), mqtt_broker);
        if (client.connect(client_id.c_str())) {
            Serial.println("connected");
            delay(20);
        } else {
            Serial.printf("failed with state %d\n", client.state());
            delay(2000);
        }
    }
    // digitalWrite(LED_BUILTIN, HIGH);
    delay(20);
}

void callback(char *topic, uint8_t *payload, unsigned int length)
{
    // digitalWrite(LED_BUILTIN, LOW);
    String msg = "";
    for (unsigned int i = 0; i < length; i++) {
        msg += (char)payload[i];
    }
    Serial.printf("receive message from topic %s: %s\n", topic, msg.c_str());
    Serial.println("--------------------------");

    if (act_on_msg(msg)) {
        // digitalWrite(LED_BUILTIN, HIGH);
    }
}

bool act_on_msg(String msg)
{
    if (msg.equals("open_door")) {
        servo.write(servo_ON);
        tft.setCursor(50, 150, 4);
        tft.setTextColor(TFT_GREEN, TFT_WHITE);
        tft.printf("OPEN!");
        Beep(true);     // 开锁成功
        if (!client.publish("/lock/response", "success")) {
            servo.write(servo_OFF);
            delay(200);
            return false;
        }
        delay(2000);
    }

    servo.write(servo_OFF);
    delay(200);
    clear();
    return true;

}